#pragma once
#include <iostream>
#include <memory>
#include "Socket.hpp"
#include <sys/epoll.h>
#include "LOG.hpp"
#include "InetAddr.h"

class EpollSever
{
    const static int gnum = 64;
    const static int EPOLL_CREATE_FAILE = 1;

public:
    EpollSever(uint16_t port)
        : _port(port), _listenfd(std::make_unique<TcpSocket>())
    {
        InetAddr addr("0", _port);
        _listenfd->BuildListenSocket(addr);
        // 创建epoll模型
        _epfd = epoll_create(128);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll create failed!");
            exit(EPOLL_CREATE_FAILE);
        }
        // 添加_listenfd到epoll模型
        struct epoll_event event;
        event.events = EPOLLIN;
        event.data.fd = _listenfd->sockfd();
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listenfd->sockfd(), &event);
        if (n < 0)
        {
            LOG(ERROR, "epoll_ctl listen fail: %s\n", strerror(errno));
            return;
        }
    }

    ~EpollSever()
    {
        ::close(_listenfd->sockfd());
        if (_epfd >= 0)
            ::close(_epfd);
    }

    void Handler(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int event = revents[i].events;
            int alreadyfd = revents[i].data.fd;

            // 读事件就绪
            if (event & EPOLLIN)
            {
                // listen套接字就绪
                if (_listenfd->sockfd() == alreadyfd)
                {
                    InetAddr client;
                    int fd = _listenfd->AcceptSocket(&client);
                    // 把新fd先添加到epoll模型中
                    struct epoll_event event;
                    event.events = EPOLLIN;
                    event.data.fd = fd;
                    int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &event);
                    if (n < 0)
                    {
                        LOG(ERROR, "epoll_ctl normal fd fail: %s\n", strerror(errno));
                        return;
                    }
                }
                // 普通套接字数据到来
                else
                {
                    char buffer[1024];
                    int n = ::recv(alreadyfd, buffer, sizeof(buffer) - 1, 0);
                    if (n > 0)
                    {
                        std::cout << "client say# " << buffer << std::endl;
                        std::string echo_string = "server echo#";
                        echo_string += buffer;
                        ::send(alreadyfd, echo_string.c_str(), echo_string.size(), 0);
                    }
                    // 对方连接关闭
                    else if (n == 0)
                    {
                        epoll_ctl(_epfd, EPOLL_CTL_DEL, alreadyfd, nullptr);
                        ::close(alreadyfd);
                        LOG(INFO, "recv: client connection have been quit! quit fd: %d", alreadyfd);
                    }
                    // 出错
                    else
                    {
                        epoll_ctl(_epfd, EPOLL_CTL_DEL, alreadyfd, nullptr);
                        ::close(alreadyfd);
                        LOG(INFO, "recv fail!");
                    }
                }
            }
        }
    }
    void loop()
    {
        while (1)
        {
            int timeout = 1000;
            int n = epoll_wait(_epfd, revents, gnum, timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "epoll_wait not have already fd timeout!");
                break;
            case -1:
                LOG(ERROR, "epoll_wait error!");
                break;
            default:
                LOG(INFO, "epoll_wait have already,now fd nums: n: %d", n);
                Handler(n);
                break;
            }
        }
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listenfd;
    int _epfd;
    struct epoll_event revents[gnum];
};